package app.config;

import app.core.encrypt.Encoders;
import com.gitee.feizns.dynamic.Lists;
import com.gitee.feizns.dynamic.decrypt.KeyPair;
import com.gitee.feizns.dynamic.decrypt.Rsa;
import com.gitee.feizns.quickstart.dict.Dict;
import com.gitee.feizns.quickstart.dict.Entry;
import com.gitee.feizns.quickstart.dict.Permission;
import com.gitee.feizns.quickstart.dict.spring.boot.autoconfigure.db.mybatis.flex.mapper.QkDictMapper;
import com.gitee.feizns.quickstart.dict.spring.boot.autoconfigure.db.mybatis.flex.pojo.QkDict;
import com.gitee.feizns.quickstart.jackson.Val;
import org.checkerframework.checker.index.qual.NonNegative;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * 配置
 * @author feizns
 * @since 2025/9/25
 */
public interface Config {


    /**
     * 默认大小
     */
    int DEFAULT_SIZE = 2048;

    /**
     * RSA 私钥
     */
    String RSA_PRIVATE_KEY = "rsa.private-key";

    /**
     * RSA 公钥
     */
    String RSA_PUBLIC_KEY = "rsa.public-key";

    /**
     * 公用密钥
     */
    Set<String> PUBLIC_KEYS = Set.of(
            "default-avatar-url",
            "role-group-names",
            "public-keys"
    );

    /**
     * 如果不存在，则放
     */
    static void putIfAbsent(String key, Object value) {
        putIfAbsent(key, value, Permission.ReadWrite);
    }

    /**
     * 如果不存在，则放
     */
    static void putIfAbsent(String key, Object value, Permission permission) {
        putIfAbsent(key, value, permission, null);
    }

    /**
     * 如果不存在，则放
     */
    static void putIfAbsent(String key, Object value, Permission permission, String description) {
        Dict.putIfAbsent(key, QkDict.of(key, value, permission, description));
    }

    /**
     * 如果不存在，则获取 put
     * @param key 钥匙
     * @param value 值
     * @param permission 许可
     * @return {@link Val }
     */
    static Val getIfAbsentPut(String key, Object value, Permission permission) {
        return getIfAbsentPut(key, value, permission, null);
    }

    /**
     * 如果不存在，则获取 put
     * @param key 钥匙
     * @param value 值
     * @param permission 许可
     * @return {@link Entry }
     */
    static Val getIfAbsentPut(String key, Object value, Permission permission, String description) {
        Entry entry = Dict.getIfAbsentPut(key, QkDict.of(key, value, permission, description));
        return Val.of(entry.getValue());
    }

    /**
     * 重新加载
     */
    static void reload() {
        QkDictMapper.findAll().forEach(Dict::put);
    }

    /**
     * 初始化
     */
    static void init() {
        Config.rasKeySize();
        Config.rsaKeysResetIntervalCron();
        Config.rsaAutomaticDecryption();
        if ( !Dict.exists(RSA_PRIVATE_KEY) || !Dict.exists(RSA_PUBLIC_KEY) )
            resetRsaKeys();
        Config.defaultAvatarUrl();
        Config.publicKeys();
        Config.passwordEncoderType();
        Config.roleGroupNames();
        Config.qkUploadBaseUrl();
        Config.qpsEnabled();
        Config.qpsLimit();
        Config.qpsPatterns();
        Config.cryptoMediaTypes();
        Config.cryptoUrlPatterns();
        Config.loginCaptchaEnabled();
        Config.loginCaptchaValidMinutes();
    }

    /**
     * 重置RSA密钥
     */
    static KeyPair resetRsaKeys() {
        KeyPair pair = Rsa.instance().generatorKeyPair(Config.rasKeySize());
        QkDict publicKey = QkDict.of(RSA_PUBLIC_KEY, pair.getBase64PublicKey(), Permission.ReadOnly);
        QkDict privateKey = QkDict.of(RSA_PRIVATE_KEY, pair.getBase64PrivateKey(), Permission.ReadOnly);
        publicKey.setDescription("RSA加密公钥");
        privateKey.setDescription("RSA加密私钥");
        Dict.put(publicKey);
        Dict.put(privateKey);
        return pair;
    }

    /**
     * RAS 密钥大小
     * @return int
     */
    static int rasKeySize() {
        return Config.getIfAbsentPut("rsa.key-size", Config.DEFAULT_SIZE, Permission.ReadOnly, "rsa密钥长度（支持1024、2048）").intValue();
    }

    /**
     * 获取 RSA 公钥
     * @return {@link String }
     */
    static String rsaPublicKey() {
        String value = Dict.value(Config.RSA_PUBLIC_KEY).textValue();
        if ( !StringUtils.hasText(value) )
            value = Config.resetRsaKeys().getBase64PublicKey();
        return value;
    }

    /**
     * 获取 RSA 私钥
     * @return {@link String }
     */
    static String rsaPrivateKey() {
        String value = Dict.value(Config.RSA_PRIVATE_KEY).textValue();
        if ( !StringUtils.hasText(value) )
            value = Config.resetRsaKeys().getBase64PrivateKey();
        return value;
    }

    /**
     * 默认头像
     * @return {@link String }
     */
    static String defaultAvatarUrl() {
        return Config.getIfAbsentPut("default-avatar-url", "/default-avatar.png", Permission.ReadOnly, "默认头像路径").textValue();
    }

    /**
     * 系统自动解密
     * @return boolean
     */
    static boolean rsaAutomaticDecryption() {
        return Config.getIfAbsentPut("rsa-automatic-decryption", false, Permission.ReadOnly, "登录等关键参数是否解密（前端需配合加密）").booleanValue();
    }

    /**
     * 公共访问的key
     * @return {@link Set }<{@link String }>
     */
    static Set<String> publicKeys() {
        return Config.getIfAbsentPut("public-keys", Config.PUBLIC_KEYS, Permission.ReadOnly, "开放访问的字典键列表").asSet(String.class).orElse(Set.of());
    }


    /**
     * 密码编码器类型
     * @return {@link String }
     */
    static String passwordEncoderType() {
        return Config.getIfAbsentPut("password-encoder-type", "plaintext", Permission.ReadOnly, "密码加密策略（plaintext=明文、支持bcrypt、argon2、base64、sha1、sha256、sha384、sha512、md5）").textValue();
    }

    /**
     * RSA密钥重置周期
     * @return {@link String }
     */
    static String rsaKeysResetIntervalCron() {
        return Config.getIfAbsentPut("rsa-keys-reset-interval-cron", "0 0 0 * * ?", Permission.ReadOnly, "rsa公私钥重置间隔（需开启定时任务注解@EnableScheduling）").textValue();
    }

    /**
     * QK 上传基本 URL
     * @return {@link String }
     */
    static String qkUploadBaseUrl() {
        return Config.getIfAbsentPut("qk-upload-base-url", "#ServerName + '/static/'", Permission.ReadOnly, "文件上传绝对路径baseUrl").textValue();
    }

    /**
     * 启用 QPS
     * @return boolean
     */
    static boolean qpsEnabled() {
        return Config.getIfAbsentPut("qps-enabled", true, Permission.ReadOnly, "是否启用全局QPS").booleanValue();
    }

    /**
     * qps patterns
     * @return boolean
     */
    static List<String> qpsPatterns() {
        return Config.getIfAbsentPut("qps-patterns", List.of("/**"), Permission.ReadOnly, "Qps拦截路径").listValue(String.class);
    }

    /**
     * QPS 限制
     * @return int
     */
    static int qpsLimit() {
        return Config.getIfAbsentPut("qps-limit", 120, Permission.ReadOnly, "全局QPS限制").intValue();
    }

    /**
     * 加密媒体类型
     * @return {@link List }<{@link MediaType }>
     */
    static List<MediaType> cryptoMediaTypes() {
        return Config.getIfAbsentPut(
                "crypto-media-types",
                        List.of(MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_HTML_VALUE, MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_XML_VALUE),
                        Permission.ReadOnly,
                        "加密请求响应的媒体类型")
                .listValue(String.class)
                .stream().map(MediaType::valueOf)
                .toList();
    }

    /**
     * 加密请求响应的路径列表
     * @return {@link List }<{@link String }>
     */
    static List<String> cryptoUrlPatterns() {
        return Config.getIfAbsentPut("crypto-url-patterns", Collections.emptyList(), Permission.ReadOnly, "加密请求响应的路径列表").listValue(String.class);
    }

    /**
     * 角色组名称
     * @return {@link Set }<{@link String }>
     */
    static Set<String> roleGroupNames() {
        return Config.getIfAbsentPut("role-group-names", Set.of("系统"), Permission.ReadOnly, "角色组列表").setValue(String.class);
    }

    /**
     * 密码编码器
     * @return {@link PasswordEncoder }
     */
    static PasswordEncoder passwordEncoder() {
        return Encoders.of(passwordEncoderType());
    }

    /**
     * 已启用验证码
     * @return boolean
     */
    static boolean loginCaptchaEnabled() {
        return Config.getIfAbsentPut("login-captcha-enabled", false, Permission.ReadOnly, "是否启用登录验证码").booleanValue();
    }

    /**
     * 验证码有效分钟（单位分钟）
     * @return long
     */
    static @NonNegative long loginCaptchaValidMinutes() {
        return Config.getIfAbsentPut("login-captcha-valid-minutes", 15, Permission.ReadOnly, "登录验证码有效期时长（单位分钟）").longValue();
    }

    /**
     * SA 令牌排除路径模式
     * @return {@link List }<{@link String }>
     */
    static List<String> saTokenExcludePathPatterns() {
        return Config.getIfAbsentPut("sa-token-exclude-path-patterns",
                Lists.of(
                        "/error",
                        "/api/v1/admin/user/login",
                        "/api/v1/config/public-key",
                        "/api/v1/qk/dict/*/value",
                        "/api/v1/qk/dict/values",
                        "/default-avatar.png",
                        "/index.html"),
                Permission.ReadOnly, "Sa-Token匿名访问路径列表（需重启）").listValue(String.class);
    }

}
